#include "ThreadFunc.h"

void invokeThreads(pthread_attr_t* attr,vector<int> thread_elements)
{
	int current_rows = currentUniverse ->height();
	vector<int> offsets(thread_amount);
	vector<int> thread_args(2 * thread_amount);

	pthread_attr_init(attr);
	for (int i = 0; i < thread_amount; i++)
	{
		thread_elements[i] = current_rows / thread_amount;
		offsets[i] = i * (current_rows / thread_amount);
	}
	thread_elements[thread_amount - 1] = current_rows / thread_amount + current_rows % thread_amount;



	for (int i = 0,j = 0; i < thread_amount; i++,j += 2)
	{
		thread_args[j] = thread_elements[i]; //thread rows
		thread_args[j + 1] = offsets[i]; //thread row starting index
	}

#ifdef PTHREAD_DEBUG
	for (int i = 0; i < thread_amount; i++)
		cout << thread_elements[i] << " ";
	cout << endl;

	for (int i = 0; i < thread_amount; i++)
		cout << offsets[i] << " ";
	cout << endl;
#endif

	for (int i = 0; i < thread_amount; i++)
		pthread_create(&p_threads[i],attr,pthreadTick,(void *)(&thread_args[0]));

	//wait for thread termination
	for (int i = 0; i < thread_amount; i++)
		pthread_join(p_threads[i], NULL);

#ifdef PTHREAD_DEBUG
	vector<char> symbols(2);
	vector<unsigned char> elements(2);
	cout << "Next Universe on process " << myrank << endl;
	symbols[0] = 'X';
	symbols[1] = 'O';
	elements[0] = 0;
	elements[1] = 1;
	nextUniverse ->print(symbols,elements);
#endif
	thread_args.clear();
	offsets.clear();
}

void *pthreadTick(void* args)
{
	int* arg_array = (int *)args;
	pthread_t me = pthread_self();
	int thread_index = -1;

	//find thread number
	for (int i = 0; i < thread_amount; i++)
	{
		if (pthread_equal(me,p_threads[i]))
		{
			thread_index = i;
			break;
		}
	}

	if (thread_index == -1)
	{
		cerr << "ERROR: Thread not found" << endl;
		exit(1);
	}

	int thread_rows = arg_array[2 * thread_index];
	int thread_starting_row = arg_array[2 * thread_index + 1];

#ifdef PTHREAD_DEBUG
	cout << "PTHREAD " << thread_index << " on process: " << myrank << " RUNNING WITH PARAMETERS " << thread_rows << "," << thread_starting_row << endl;
#endif

	for (int i = max(1,thread_starting_row); i < min(currentUniverse ->height() - 1,i + thread_rows); i++)
	{
		for (int j = 1; j < currentUniverse ->width() - 1; j++)
		{
			int liveNeighbors =		currentUniverse -> get(i - 1, j - 1) +
									currentUniverse	-> get(i - 1, j) +
									currentUniverse -> get(i - 1, j + 1) +
									currentUniverse -> get(i, j - 1) +
									currentUniverse -> get(i, j + 1) +
									currentUniverse -> get(i + 1, j - 1) +
									currentUniverse -> get(i + 1, j) +
									currentUniverse -> get(i + 1, j + 1);
			
			if (currentUniverse -> get(i,j) == 1)
			{
				if (liveNeighbors < 2 || liveNeighbors > 3)
					nextUniverse -> set(i, j, 0);
			}
			else
				if (liveNeighbors == 3)
					nextUniverse -> set(i, j, 1);
		}
	}
#ifdef PTHREAD_DEBUG
	cout << "PThread " << thread_index << " on process " << myrank << " exited correctly..." << endl;
#endif
	pthread_exit(0);
}

